﻿namespace Jiubang.Authentications {
    using System;
    using System.Web;

    public sealed class Authentication {
        public readonly static string AccessActionName = "access";
        private const string UserSessionKey = "_UserSession";
        private readonly IPermission permission;
        private readonly IUserSessionFactory userSessionFactory;
        private readonly ILogger logger;

        /// <summary>
        /// 对用户验证事件进行记录
        /// </summary>
        [Flags]
        public enum LogMode : byte {
            /// <summary>
            /// 不写入日志
            /// </summary>
            None = 0,
            /// <summary>
            /// 验证成功时写入日志
            /// </summary>
            Success = 1,
            /// <summary>
            /// 验证失败写入日志
            /// </summary>
            Fail = 2
        }

        /// <param name="userSessionFactory">用户会话工厂对象</param>
        /// <param name="permission">访问权限验证对象</param>
        /// <param name="logger">日志对象</param>
        public Authentication(IUserSessionFactory userSessionFactory, IPermission permission, ILogger logger) {
            if (userSessionFactory == null)
                throw new ArgumentNullException("userSessionFactory");
            if (permission == null)
                throw new ArgumentNullException("permission");
            if (logger == null)
                throw new ArgumentNullException("logger");

            this.userSessionFactory = userSessionFactory;
            this.permission = permission;
            this.logger = logger;
            PermissionDeniedPage = "~/PermissionDenied.aspx";
        }

        /// <summary>
        /// 设置或获取访问权限不足时的提示页
        /// </summary>
        public string PermissionDeniedPage { get; set; }

        /// <summary>
        /// 请求上下文完成用户状态获取后事件的绑定方法。
        /// 在HttpModule验证用户权限后执行一次。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        public void OnAcquireRequestState(object sender, EventArgs args) {
            HttpContext context = ((HttpApplication)sender).Context;
            UserSession user = userSessionFactory.GetUserSession();
            context.Items[UserSessionKey] = user ?? UserSession.Guest;

            if (!permission.Ask(user, context.Request.Path, AccessActionName))
                context.RewritePath(PermissionDeniedPage, false);
        }

        private static UserSession CurrentUserSession {
            get {
                var value = HttpContext.Current.Items[UserSessionKey] as UserSession;
                if (value == null)
                    throw new NullReferenceException("获取用户会话信息前未绑定AcquireRequestState事件");
                return value;
            }
        }

        /// <summary>
        /// 请求对指定页面某一动作的操作权限，并选择是否记录用户该请求。
        /// </summary>
        /// <param name="path">请求的路径</param>
        /// <param name="action">动作名称</param>
        /// <param name="mode">日志模式</param>
        /// <returns>权限验证是否通过</returns>
        public bool Require(string path, string action, LogMode mode) {
            if (path == null)
                throw new ArgumentNullException("path");
            if (action == null)
                throw new ArgumentNullException("action");

            var userSession = CurrentUserSession;
            var allow = permission.Ask(userSession, path, action);
            if (allow) {
                if ((mode & LogMode.Success) == LogMode.Success) {
                    logger.Log(userSession, path, action, LogMode.Success.ToString());
                }
            }else if ((mode&LogMode.Fail)==LogMode.Fail) {
                logger.Log(userSession, path, action, LogMode.Fail.ToString());
            }
            return allow;
        }
    }
}